#include "../include/WebPageQuery.h" 
#include "../include/WebPage.h" 
#include "../log/Logger.hpp" 
#include"../include/MysqlTool.h"
#include<fstream>
#include<sstream>
#include<algorithm>
#include <iterator>
#include <unistd.h>
#include <random>
#include <algorithm>

//#include "../include/WebPageSearcher.h" 

using std::string;
using std::unordered_map;
using std::unordered_set;
using std::vector;
using std::pair;
using std::set;
using std::ifstream;
using std::cerr;
using std::istringstream;
using std::sort;
using std::pow;
using std::sqrt;
using std::make_pair;
using std::stoi;
using std::stod;
using std::set_intersection;
using std::inserter;
using std::find_if;



 // 构造函数的实现
WebPageQuery::WebPageQuery()
{
   
}

// 执行查询，返回结果的实现
string WebPageQuery::doQuery(const string& str,SplitToolCppJieba&  jieba)
 {
    //利用jeiba实现分词，并将分词的结果放在vector类型的queryWords中
    vector<string> jiebaWords= jieba.cut(str);

    // 读入停用词,并去除停用词
    vector<string> queryWords;
    auto stopWords = Configuration::getInstance().getChineseStopWordList();

    //为什么要插入个空
    stopWords.insert(" ");
    for(const auto& word:jiebaWords)
    {
        //停用词里找不到就插入queryWords
        if (stopWords.find(word) == stopWords.end())
        {
            queryWords.push_back(word);
        }
    }

    //resultVec用来保存每篇文章的id，以及文章中包含的关键词权重
     vector<pair<int, vector<double>>> resultVec;

    //执行查询，会得到最后的包含关键词的文章。
    bool queryResult=executeQuery(queryWords,resultVec);

    //查询结果为FALSE，表明没有对应的查询结果
    if(!queryResult)
    {
        //返回无结果的查询响应的实现
        return  returnNoAnswer();
    }
    
    string ret("succeed in");

    return ret;

}



// 根据具体情况计算词的逆文档频率
double WebPageQuery::calculateIDF(const string& word)
{
    //词汇，以及包含该词语的文档数量
    map<string, int> documentCollection;
    for(const auto& elem:_invertIndexTable)
    {
        documentCollection.emplace(elem.first,elem.second.size());
    }

    //文档总数
    int documentCount=_count
    ;

    // 统计包含该词语的文档数量（DF）
    int documentFrequency = documentCollection[word];

    // 计算逆文档频率（IDF）
    double idf = log2(static_cast<double>(documentCount) / (documentFrequency + 1));

    return idf;
}

// 计算查询词的权重值的实现
vector<double> WebPageQuery::getQueryWordsWeightVector(const vector<string>& queryWords) 
{
    vector<double> weights;
    

    // 计算每个词的权重系数
    for (const string& word : queryWords) {
        double tf = 1;
        double idf = calculateIDF(word);  // 根据具体情况计算词的逆文档频率

        double weight = tf * idf;
        weights.push_back(weight);
    }

    // 归一化处理
    double sumOfSquares = 0.0;
    for (double weight : weights) {
        sumOfSquares += pow(weight, 2);
    }

    double normalizationFactor = sqrt(sumOfSquares);

    for (double& weight : weights) {
        weight /= normalizationFactor;
    }

    return weights;
}

//根据分词后的queryWords得到包含文章关键词的倒排索引库内容，文章ID等
bool WebPageQuery::setInvertIndexTable(const vector<string>& queryWords, const string &filename)
{
    //方式1.setInvertIndexTable 打开倒排索引库文件查询
#ifdef 1
    ifstream ifs(filename);
    if(!ifs)
    {
        cerr << "ifs open " << filename << " error!" << endl;
        return false;
    }

    string line;
    while(getline(ifs,line))
    {
        istringstream iss(line);
        string word;
        iss>>word;//处理关键词

        //防空警报 1639 0.033625 1664 0.083116
        
        for(auto &elem :queryWords)//判断库中词汇是否匹配待搜索关键词
        {
            if(elem==word)
            {  
                string id;
                string weight;
                while(iss>>id,iss>>weight)//循环提取文章id和权重，并放与map中
                {
                    _invertIndexTable[word].insert(make_pair(stoi(id),stod(weight)));
                }
            }
        } 
    }

    //_invertIndexTable初始化完成：所有包含关键字的文章id的集合map<string,set<pair<int,double>>>
    
    //没有同时命中这几个关键词的文章返回失败，_invertIndexTable
    if(queryWords.size()!=_invertIndexTable.size())
    {
        return false;
    }

    ifs.close();

#endif

#ifdef 0
    //方式1 end

    //方式2.使用Mysql优化从Mysql表中查询

    Mysql_Tool mysqltool("tcp://127.0.0.1:3306","root","18785524802");
    mysqltool.read_invert_index_sql(queryWords,this->_invertIndexTable);
    if(queryWords.size()!=_invertIndexTable.size())
    {
        return false;
    }
    //方式2 end
#endif
    return true;

}

//求不同关键字都有的id，及id的交集
void  WebPageQuery::setCommonId()
{
    //遍历_invertIndexTable
    for(const auto& elem : _invertIndexTable)
    {
        const auto& word=elem.first;
        const auto& valueSet=elem.second;

        // 如果 commonId 为空，则将当前关键词的所有ID放入 commonId
        if(_commonId.empty())
        {
            for(const auto& value :valueSet)
            {
                _commonId.insert(value.first);
            }
        }
        else
        {
            // 创建一个临时的集合，用于存储当前关键词的ID
            set<int> tempId;
            for(const auto& value :valueSet)
            {
                tempId.insert(value.first);
            }

            // 取 commonId 和当前关键词的ID的交集
            set<int> intersection;
            set_intersection(_commonId.begin(),_commonId.end(),tempId.begin(),tempId.end(),
                             inserter(intersection,intersection.begin()));
            
            //更新交集到commonId中
            _commonId=intersection;
        }
    }

        //超过50个id则随机抽取50个
    set<int> result;
    if(_commonId.size()>50)
    {
        vector<int> temp(_commonId.begin(),_commonId.end());
        std::random_device rd;
        std::mt19937 rng(rd());
        std::shuffle(temp.begin(), temp.end(), rng);
        for (int i = 0; i < 50; i++) 
        {
            result.insert(temp[i]);
        }
        _commonId=result;
    }

}

//vector<pair<int, vector<double>>>& resultVec生成这个，方便后面进行排名
void WebPageQuery::setReseltVec(vector<pair<int, vector<double>>>& resultVec)
{
    //遍历commonId中的每个id
    for(const auto& id:_commonId)
    {
        vector<double> weights;
        //遍历_invertIndexTable
        for(const auto& elem :_invertIndexTable)
        {
            const auto& valueSet=elem.second;
            //将valueSet中的每个pair给p，判断p.first是不是要找到id
            auto it=find_if(valueSet.begin(),valueSet.end(),[id](const pair<int,double>& p)
                                                            {
                                                              return p.first==id;  
                                                            }
                            );
            // 如果找到匹配的条目，则将其权重存储到 weights中
            if (it != valueSet.end()) {
                weights.push_back(it->second);
            }
        }

        // 将 ID 和对应的权重添加到 resultVec 中
        resultVec.emplace_back(id, weights);
    }  
     
}

//根据文章id和偏移库内容，确定文章位置
void WebPageQuery::setOffSetLib(const string &filename)
{
    //打开文件
    ifstream ifs(filename);
    if(!ifs)
    {
        cerr << "ifs open " << filename << " error!" << endl;
        return;
    }

   
    string line;
    while(getline(ifs,line))//没到最后一行且ID没有全部找到
    {
        istringstream iss(line);
        string strId;
        iss>>strId;
        int id=stoi(strId);

        auto it=_commonId.find(id);
        if(it!=_commonId.end())
        {
            string startAdd;
            string len;
            iss>>startAdd,iss>>len;
            _offsetLib.emplace(id,make_pair(stoi(startAdd),stoi(len)));
        }
        _count++;
    }

    ifs.close();
}

//确定文章位置后，利用网页库，生成对应文章的WebPage对象
void WebPageQuery::setPageLib(const vector<string>& queryWords,const string &filename)
{
    //打开文件
    ifstream ifs(filename);
    if(!ifs)
    {
        cerr << "ifs open " << filename << " error!" << endl;
        return;
    }

    for(const auto& elem:_offsetLib)
    {
        const auto& id=elem.first;
        const auto& start_len=elem.second;

        //定位到开始位置
        ifs.seekg(start_len.first);  

        //读取字符到字符串doc
        char* buf=new char[start_len.second+1];
        ifs.read(buf,start_len.second);
        buf[start_len.second]='\0';
        string doc(buf);  
        delete [] buf;

        //注意id是字符还是数字类型

        WebPage webContent(queryWords,doc);
        _pageLib.emplace(id,webContent);
    }

    ifs.close();
}

// 计算余弦相似度
double calculateCosineSimilarity(const vector<double>& x, const vector<double>& y) 
{
    // 确保向量维度相同
    if (x.size() != y.size()) {
        cerr << "向量维度不匹配！" << std::endl;
        return 0.0;
    }

    double dotProduct = 0.0;
    double normX = 0.0;
    double normY = 0.0;

    // 计算点积和各向量的范数
    for (size_t i = 0; i < x.size(); i++) {
        dotProduct += x[i] * y[i];
        normX += pow(x[i], 2);
        normY += pow(y[i], 2);
    }

    // 计算余弦相似度
    double similarity = dotProduct / (sqrt(normX) * sqrt(normY));
    return similarity;
}

//余弦相似度
void WebPageQuery::cosineSimilarity(vector<double>& x,vector<pair<int, vector<double>>>& resultVec)
{
    
     // 计算每个网页向量与基准向量的余弦相似度
    for (const auto& elem : resultVec) 
    {
        int docId = elem.first;
        const vector<double>& y = elem.second; // 网页向量 Y

        // 计算余弦相似度
        double similarity = calculateCosineSimilarity(x, y);
        // 存储文档ID和相似度的一对数据到向量中
        _similarityVec.push_back(make_pair(docId, similarity));
    }

    // 根据相似度进行降序排序
    sort(_similarityVec.begin(), _similarityVec.end(),
              [](const pair<int, double>& a, const pair<int, double>& b) 
              {
                  return a.second > b.second;
              });

}

// 执行查询的实现 resultVec为传入传出
bool WebPageQuery::executeQuery(const vector<string>& queryWords, vector<pair<int, vector<double>>>& resultVec) 
{
    //根据分词后的queryWords得到包含文章关键词的倒排索引库内容，文章ID等
    bool ret=setInvertIndexTable(queryWords,Configuration::getInstance().getInvertIndxPath());
    
    //词库中没有要查找的词汇
    if(!ret)
    {
        return false;
    }
    //就不同关键字的id交集
    setCommonId();

    //找不到拥有所有关键词的文章id
    if(0==_commonId.size())
    {
        return false;
    }

    //根据文章id和偏移库内容，确定文章位置
    setOffSetLib(Configuration::getInstance().getOffsetFilePath());

    //确定文章位置后，利用网页库，生成对应文章的WebPage对象
    setPageLib(queryWords,Configuration::getInstance().getFormatedWebPagePath());

    //resultVec存放若干个y向量，生成一下，方便后面进行排名
    setReseltVec(resultVec);

    //计算查询词的权重值的实现
    vector<double> x=getQueryWordsWeightVector(queryWords);

    //计算x向量和y向量的余弦相似度,并排序放置于_similarityVec
    cosineSimilarity(x,resultVec);

    return true;

}

// string WebPageQuery::createJson(const vector<int>& docIdVec, const vector<string>& queryWords) {
//     // 创建 JSON 格式的查询结果的实现
//     // ...
// }

string WebPageQuery::returnNoAnswer() {
    // 返回无结果的查询响应的实现
    // ...
    string ret("NoAnswer");
    return ret;
}

void WebPageQuery::getresult(vector<string> &vec_result){
    for(auto& elem :_similarityVec)
    {
        string stmp;
        // cout<<endl;
        stmp+="id=";
        char ctmp[20]={0};
        sprintf(ctmp,"%d\n",_pageLib[elem.first].getDocId());
        stmp+=ctmp;
        // cout<<"id="<<_pageLib[elem.first].getDocId()<<endl;
        stmp+="Title="+_pageLib[elem.first].getTitle()+'\n';
        //cout<<"Title="<<_pageLib[elem.first].getTitle()<<endl;
        stmp+="URL="+_pageLib[elem.first].getURL()+'\n';
        //cout<<"URL="<<_pageLib[elem.first].getURL()<<endl;
        stmp+="摘要="+_pageLib[elem.first].getSummary()+'\n';
        vec_result.push_back(stmp);
    } 
}